#include "platform_verify.h"
#include <errno.h>
#include <ctype.h>

static char* extract_json_string_value(const char* json, const char* key)
{
    char* value = NULL;
    char search_key[128];
    snprintf(search_key, sizeof(search_key), "\"%s\":", key);
    
    char* pos = strstr(json, search_key);
    if (pos) {
        pos = strchr(pos + strlen(search_key), '"');
        if (pos) {
            pos++;
            char* end = strchr(pos, '"');
            if (end) {
                size_t len = end - pos;
                value = (char*)malloc(len + 1);
                if (value) {
                    strncpy(value, pos, len);
                    value[len] = '\0';
                }
            }
        }
    }
    return value;
}

static char* read_file_content(const char* file_path)
{
    FILE* file = fopen(file_path, "r");
    if (!file) {
        printf("Failed to open file: %s\n", file_path);
        return NULL;
    }
    
    fseek(file, 0, SEEK_END);
    long file_size = ftell(file);
    fseek(file, 0, SEEK_SET);
    
    if (file_size <= 0) {
        printf("Invalid file size: %ld\n", file_size);
        fclose(file);
        return NULL;
    }
    
    char* content = (char*)malloc(file_size + 1);
    if (!content) {
        printf("Failed to allocate memory for file content\n");
        fclose(file);
        return NULL;
    }
    
    size_t read_size = fread(content, 1, file_size, file);
    content[read_size] = '\0';
    fclose(file);
    
    return content;
}

static bool parse_measure_values(const char* json_content, platform_ref_values_t* ref_values)
{
    char* measure_start = strstr(json_content, "\"measure_value\"");
    if (!measure_start) {
        printf("measure_value not found in JSON\n");
        return false;
    }
    
    char* array_start = strchr(measure_start, '[');
    if (!array_start) {
        printf("measure_value array start '[' not found\n");
        return false;
    }
    
    char* array_end = strchr(array_start, ']');
    if (!array_end) {
        printf("measure_value array end ']' not found\n");
        return false;
    }
    
    size_t object_count = 0;
    char* pos = array_start;
    while (pos < array_end && (pos = strchr(pos, '{')) != NULL) {
        object_count++;
        pos++;
    }
    
    if (object_count == 0) {
        printf("No objects found in measure_value array\n");
        return false;
    }
    
    ref_values->sw_components = (sw_component_ref_t*)malloc(object_count * sizeof(sw_component_ref_t));
    if (!ref_values->sw_components) {
        printf("Failed to allocate memory for sw_components\n");
        return false;
    }
    ref_values->sw_comp_count = object_count;
    
    pos = array_start;
    size_t index = 0;
    while (pos < array_end && index < object_count) {
        char* obj_start = strchr(pos, '{');
        if (!obj_start) {
            break;
        }
        
        char* obj_end = strchr(obj_start, '}');
        if (!obj_end) {
            break;
        }
        
        size_t obj_len = obj_end - obj_start + 1;
        char* obj_content = (char*)malloc(obj_len + 1);
        if (!obj_content) {
            printf("Failed to allocate memory for object content\n");
            break;
        }
        
        strncpy(obj_content, obj_start, obj_len);
        obj_content[obj_len] = '\0';
        
        char* firmware_name = extract_json_string_value(obj_content, "firware_name");
        char* measurement = extract_json_string_value(obj_content, "measurement");
        char* firmware_version = extract_json_string_value(obj_content, "firware_version");
        char* hash_algorithm = extract_json_string_value(obj_content, "hash_algorithm");
        
        if (firmware_name && measurement && firmware_version && hash_algorithm) {
            strncpy(ref_values->sw_components[index].firmware_name, firmware_name,
                    sizeof(ref_values->sw_components[index].firmware_name) - 1);
            strncpy(ref_values->sw_components[index].measurement, measurement,
                    sizeof(ref_values->sw_components[index].measurement) - 1);
            strncpy(ref_values->sw_components[index].firmware_version, firmware_version,
                    sizeof(ref_values->sw_components[index].firmware_version) - 1);
            strncpy(ref_values->sw_components[index].hash_algorithm, hash_algorithm,
                    sizeof(ref_values->sw_components[index].hash_algorithm) - 1);
            
            index++;
        } else {
            printf("Failed to parse component %zu - missing required fields\n", index);
        }
        
        if (firmware_name) {
            free(firmware_name);
        }
        if (measurement) {
            free(measurement);
        }
        if (firmware_version) {
            free(firmware_version);
        }
        if (hash_algorithm) {
            free(hash_algorithm);
        }
        free(obj_content);
        
        pos = obj_end + 1;
    }
    
    ref_values->sw_comp_count = index;
    return index > 0;
}

bool load_platform_ref_values(const char *json_file_path, platform_ref_values_t *ref_values)
{
    if (!json_file_path || !ref_values) {
        printf("Invalid parameters\n");
        return false;
    }
    
    memset(ref_values, 0, sizeof(platform_ref_values_t));
    
    char* json_content = read_file_content(json_file_path);
    if (!json_content) {
        return false;
    }
    
    bool result = parse_measure_values(json_content, ref_values);
    
    free(json_content);
    return result;
}

static bool compare_hex_strings(const char* hex1, const char* hex2)
{
    if (!hex1 || !hex2) {
        return false;
    }
    
    size_t len1 = strlen(hex1);
    size_t len2 = strlen(hex2);
    if (len1 != len2) {
        return false;
    }
    
    for (size_t i = 0; i < len1; i++) {
        if (tolower(hex1[i]) != tolower(hex2[i])) {
            return false;
        }
    }
    
    return true;
}

static char* qbuf_to_hex_string(const qbuf_t* buf)
{
    if (!buf || !buf->ptr || buf->len == 0) {
        return NULL;
    }
    
    char* hex_str = (char*)malloc(buf->len * 2 + 1);
    if (!hex_str) {
        return NULL;
    }
    
    for (size_t i = 0; i < buf->len; i++) {
        sprintf(hex_str + (i * 2), "%02x", ((unsigned char*)buf->ptr)[i]);
    }
    hex_str[buf->len * 2] = '\0';
    
    return hex_str;
}

static char* qbuf_to_string(const qbuf_t* buf)
{
    if (!buf || !buf->ptr || buf->len == 0) {
        return NULL;
    }
    
    char* str = (char*)malloc(buf->len + 1);
    if (!str) {
        return NULL;
    }
    
    memcpy(str, buf->ptr, buf->len);
    str[buf->len] = '\0';
    
    return str;
}

static bool verify_single_sw_component(const sw_comp_claims_t* token_comp,
                                       const sw_component_ref_t* ref_comp,
                                       const char* component_name,
                                       uint64_t component_index)
{
    bool match = true;
    bool has_mismatch = false;
    
    char* token_type = qbuf_to_string(&token_comp->component_type);
    if (!token_type) {
        printf("Component %lu (%s): Failed to extract component type from token\n",
               component_index, component_name);
        return false;
    }
    
    if (strcmp(token_type, ref_comp->firmware_name) != 0) {
        if (!has_mismatch) {
            printf("Component %lu (%s) verification FAILED:\n", component_index, component_name);
            has_mismatch = true;
        }
        printf("  Component Type: MISMATCH (token: %s, ref: %s)\n",
               token_type, ref_comp->firmware_name);
        match = false;
    }
    free(token_type);
    
    char* token_measurement = qbuf_to_hex_string(&token_comp->measurement);
    if (!token_measurement) {
        printf("Component %lu (%s): Failed to extract measurement from token\n",
               component_index, component_name);
        return false;
    }
    
    if (!compare_hex_strings(token_measurement, ref_comp->measurement)) {
        if (!has_mismatch) {
            printf("Component %lu (%s) verification FAILED:\n", component_index, component_name);
            has_mismatch = true;
        }
        printf("  Measurement: MISMATCH\n");
        printf("    Token:     %s\n", token_measurement);
        printf("    Reference: %s\n", ref_comp->measurement);
        match = false;
    }
    free(token_measurement);
    
    char* token_version = qbuf_to_string(&token_comp->version);
    if (!token_version) {
        printf("Component %lu (%s): Failed to extract version from token\n",
               component_index, component_name);
        return false;
    }
    
    if (strcmp(token_version, ref_comp->firmware_version) != 0) {
        if (!has_mismatch) {
            printf("Component %lu (%s) verification FAILED:\n", component_index, component_name);
            has_mismatch = true;
        }
        printf("  Version: MISMATCH (token: %s, ref: %s)\n",
               token_version, ref_comp->firmware_version);
        match = false;
    }
    free(token_version);
    
    return match;
}

bool verify_platform_sw_components(const platform_claims_t *platform_claims,
                                   const platform_ref_values_t *ref_values)
{
    if (!platform_claims || !ref_values) {
        printf("Invalid parameters for sw-components verification\n");
        return false;
    }
    
    if (!platform_claims->sw_components || platform_claims->sw_comp_cnts == 0) {
        printf("No software components found in platform token\n");
        return false;
    }
    
    if (!ref_values->sw_components || ref_values->sw_comp_count == 0) {
        printf("No reference values loaded\n");
        return false;
    }
    
    bool overall_result = true;
    size_t matched_count = 0;
    
    /*
     * iterate through each component in token,
     * find matching component in reference values
     */
    for (uint64_t i = 0; i < platform_claims->sw_comp_cnts; i++) {
        const sw_comp_claims_t* token_comp = &platform_claims->sw_components[i];
        
        char* token_type = qbuf_to_string(&token_comp->component_type);
        if (!token_type) {
            printf("Failed to extract component type from token component %lu\n", i);
            overall_result = false;
            continue;
        }
        
        bool found_match = false;
        for (size_t j = 0; j < ref_values->sw_comp_count; j++) {
            const sw_component_ref_t* ref_comp = &ref_values->sw_components[j];
            
            if (strcmp(token_type, ref_comp->firmware_name) == 0) {
                if (verify_single_sw_component(token_comp, ref_comp, token_type, i)) {
                    matched_count++;
                    found_match = true;
                } else {
                    overall_result = false;
                }
                break;
            }
        }
        
        if (!found_match) {
            printf("Component %lu (%s) verification FAILED:\n", i, token_type);
            printf("  No matching reference component found for: %s\n", token_type);
            overall_result = false;
        }
        
        free(token_type);
    }
    
    return overall_result;
}

void free_platform_ref_values(platform_ref_values_t *ref_values)
{
    if (ref_values && ref_values->sw_components) {
        free(ref_values->sw_components);
        ref_values->sw_components = NULL;
        ref_values->sw_comp_count = 0;
    }
}